home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / ups / genpower.000 / genpower / genpower-1.0.1 / genpowerd.c < prev    next >
C/C++ Source or Header  |  1995-07-16  |  22KB  |  449 lines

  1. /************************************************************************/
  2. /* File Name            : genpowerd.c                                   */
  3. /* Program Name         : genpowerd                   Version: 1.0.1    */
  4. /* Author               : Tom Webster <webster@kaiwan.com>              */
  5. /* Created              : 1994/04/20                                    */
  6. /* Last Modified By     : Tom Webster                 Date: 1995/07/05  */
  7. /*                                                                      */
  8. /* Compiler (created)   : GCC 2.6.3                                     */
  9. /* Compiler (env)       : Linux 1.2.5                                   */
  10. /* ANSI C Compatable    : No                                            */
  11. /* POSIX Compatable     : Yes?                                          */
  12. /*                                                                      */
  13. /* Purpose              : Monitor the serial port connected to a UPS.   */
  14. /*                      : Functions performed depend on the values in   */
  15. /*                      : the genpowerd.h file and command line         */
  16. /*            : configuration.  As a minimum, the power       */
  17. /*                      : status will be monitored.  Low battery        */
  18. /*                      : sensing and physical cable conection checks   */
  19. /*                      : are also supported.                           */
  20. /*                      :                                               */
  21. /*                      : If one of the monitored lines of the serial   */
  22. /*                      : connection changes state, genpowerd will      */
  23. /*                      : notify init.                                  */
  24. /*                      :                                               */
  25. /*                      : If sent the "-k" option (and configured to    */
  26. /*                      : support shutting the inverter down), will     */
  27. /*                      : shut the inverter down (killing power to      */
  28. /*                      : the system while in powerfail mode).  If      */
  29. /*            : not configured to shutdown the inverter, or   */
  30. /*            : if unable to, genpowerd will return a         */
  31. /*            : non-zero exit code.                           */
  32. /*                                                                      */
  33. /* Usage                : genpowerd [-k] <serial device> <ups-type>     */
  34. /*                      : i.e. genpowerd /dev/cua4 tripp-nt             */
  35. /*                      : or:  genpowerd  -k /dev/cua4 tripp-nt         */
  36. /*                                                                      */
  37. /* History/Credits      : genpowerd was previously known as unipowerd.  */
  38. /*                      :                                               */
  39. /*                      : genpowerd is a hack to make the powerd        */
  40. /*                      : deamon easier to configure and to add         */
  41. /*                      : additional features.                          */
  42. /*                      :                                               */
  43. /*                      : powerd was written by Miquel van Smoorenburg  */
  44. /*                      : <miquels@drinkel.nl.mugnet.org> for his       */
  45. /*                      : employer Cistron Electronics and released     */
  46. /*                      : into the public domain.                       */
  47. /*                                                                      */
  48. /* Copyright            : GNU Copyleft                                  */
  49. /************************************************************************/
  50.  
  51. #include <sys/types.h>
  52. #include <sys/ioctl.h>
  53. #include <fcntl.h>
  54. #include <errno.h>
  55. #include <stdlib.h>
  56. #include <unistd.h>
  57. #include <stdio.h>
  58. #include <signal.h>
  59. #include <syslog.h>
  60. #include "genpowerd.h"
  61.  
  62.  
  63. char *str_line(int l)
  64. {
  65.   switch(l){  
  66.   case TIOCM_RTS: return "RTS";
  67.   case TIOCM_CTS: return "CTS";
  68.   case TIOCM_DTR: return "DTR";
  69.   case TIOCM_CAR: return "CAR";
  70.   case TIOCM_RNG: return "RNG";
  71.   case TIOCM_DSR: return "DSR";
  72.   case TIOCM_ST:  return "ST ";
  73.   default:        return "---";
  74.   }
  75. }
  76.  
  77. char *str_neg(int s)
  78. {
  79.   if (s) return "*"; else return " ";
  80. }
  81.  
  82. /* make a table of all supported UPS types */
  83. void list_ups()
  84. {
  85.  
  86.   fprintf(stderr,"\n    <ups-type>    cablep. kill  t powerok battok cableok\n");
  87.   fprintf(stderr,"    ----------------------------------------------------\n");
  88.   for (pups=&ups[0]; pups->tag; pups++){
  89.       fprintf(stderr,"    %-12s  %s%s    %s%s %2d %s%s    %s%s   %s%s\n",pups->tag,
  90.         str_neg(pups->cablepower.inverted),str_line(pups->cablepower.line),
  91.         str_neg(pups->kill.inverted),str_line(pups->kill.line),
  92.         pups->killtime,
  93.         str_neg(pups->powerok.inverted),str_line(pups->powerok.line),
  94.         str_neg(pups->battok.inverted),str_line(pups->battok.line),
  95.         str_neg(pups->cableok.inverted),str_line(pups->cableok.line)
  96.       );
  97.   }
  98.   fprintf(stderr,"           (*=active low, ---=unused)\n\n");
  99. }
  100.  
  101. int getlevel(LINE *l,int flags);
  102. void setlevel(int fd,int line,int level);
  103. void powerfail(int);
  104.  
  105.  
  106. /* Main program. */
  107. int main(int argc, char **argv) {
  108.         int fd, fd2;
  109.         int flags;
  110.         int pstatus, poldstat = 1;
  111.         int bstatus, boldstat = 1;
  112.         int count = 0;
  113.         int tries = 0;
  114.         int ikill = 0;
  115.         char *program_name;
  116.  
  117.  
  118.         program_name = argv[0];
  119.         /* Parse the command line */
  120.         while ((argc > 1) && (argv[1][0] == '-')) {
  121.                 switch (argv[1][1]) {
  122.                   case 'k':
  123.                         ikill = 1;
  124.                         break;
  125.                   default:
  126.                         fprintf(stderr, "\nUsage: %s [-k] <device> <ups-type>\n", program_name);                                             
  127.                         exit(1);
  128.                 }                                       /* switch (argv[1][1]) */
  129.                 argv++;
  130.                 argc--;
  131.         }                                               /* while ((argc > 1) && (argv[1][0] == '-')) */
  132.         
  133.         /* Done with options, make sure that one port is specified */
  134.         if (argc != 3) {
  135.                 fprintf(stderr, "\nUsage: %s [-k] <device> <ups-type>\n", program_name);
  136.                 list_ups();  
  137.                 exit(1);
  138.         }                                        /* if (argc != 3) */
  139.  
  140.         
  141.         for (pups=&ups[0]; pups->tag; pups++){
  142.                 if (strcmp(pups->tag,argv[2])==0) break;
  143.         }                        /* for (pups=&ups[0]; pups->tag; pups++) */
  144.         if (!pups->tag){
  145.                 fprintf(stderr, "Error: %s: UPS <%s> unknown\n", program_name,argv[2]);
  146.                 exit(1);
  147.         }                        /* if (!pups->tag) */
  148.         
  149.                 
  150.         /* Kill the inverter and close out if inverter kill was selected */
  151.         if (ikill) {
  152.                 if (pups->killtime) {
  153.                        if ((fd = open(argv[1], O_RDWR | O_NDELAY)) < 0) {
  154.                                 fprintf(stderr, "%s: %s: %s\n",program_name, argv[1], sys_errlist[errno]);
  155.                                 exit(1);
  156.                         }                 /* if ((fd = open(argv[1], O_RDWR | O_NDELAY)) < 0) */
  157.  
  158.             /* Explicitly clear both DTR and RTS as soon as possible  */
  159.                 ioctl(fd, TIOCMBIC, TIOCM_RTS);
  160.                 ioctl(fd, TIOCMBIC, TIOCM_DTR);
  161.  
  162.                 /* clear killpower, apply cablepower to enable monitoring */
  163.                 setlevel(fd,pups->kill.line,!pups->kill.inverted);
  164.                   setlevel(fd,pups->cablepower.line,!pups->cablepower.inverted);
  165.         
  166.             if (pups->kill.line == TIOCM_ST) {
  167.                             /* Send BREAK (TX high) for INVERTERTIME seconds to kill the UPS inverter. */
  168.                             ioctl(fd, TCSBRKP, 10*pups->killtime);
  169.             } else {
  170.                             /* Force high to send the UPS the inverter kill signal. */
  171.                             setlevel(fd,pups->kill.line,pups->kill.inverted);
  172.                             sleep(pups->killtime);
  173.             }                /* if (pups->kill.line == TIOCM_ST) */
  174.                     ioctl(fd, TIOCMGET, &flags);
  175.                            close(fd);
  176.  
  177.                         /************************************************************/
  178.                         /* We never should have gotten here.                        */
  179.                         /* The inverter kill has failed for one reason or another.  */
  180.                         /* If still in powerfail mode, exit with an error.          */
  181.                         /* If power is ok (power has returned) let rc.0 finish the  */
  182.                         /* reboot.                                                  */
  183.                         /************************************************************/
  184.                         if (getlevel(&pups->powerok,flags) == 0) {
  185.                                 fprintf(stderr, "%s: UPS inverter kill failed.\n", program_name);
  186.                                 exit(1);
  187.                         }                /* if (getlevel(&pups->powerok,flags) == 0) */
  188.  
  189.             /* Otherwise, exit normaly, power has returned. */
  190.                            exit(0);
  191.                 } else {
  192.                         fprintf(stderr, "Error: %s: UPS <%s> has no support for killing the inverter.\n", 
  193.                                         program_name,pups->tag);
  194.                         exit(1);
  195.                 }                                       /* if (pups->kill) */
  196.         }                                               /* if (ikill) */
  197.  
  198.         /****************************************/
  199.         /* If no kill signal, monitor the line. */
  200.         /****************************************/
  201.  
  202.         /* Start syslog. */
  203.         openlog(program_name, LOG_CONS|LOG_PERROR, LOG_DAEMON);
  204.  
  205.         /* Open monitor device. */
  206.         if ((fd = open(argv[1], O_RDWR | O_NDELAY)) < 0) {
  207.                 syslog(LOG_ERR, "%s: %s", argv[1], sys_errlist[errno]);
  208.                 closelog();
  209.                 exit(1);
  210.         }                         /* if ((fd = open(argv[1], O_RDWR | O_NDELAY)) < 0) */
  211.  
  212.    
  213.     /* Explicitly clear both DTR and RTS as soon as possible  */
  214.         ioctl(fd, TIOCMBIC, TIOCM_RTS);
  215.         ioctl(fd, TIOCMBIC, TIOCM_DTR);
  216.  
  217.         /* clear killpower, apply cablepower to enable monitoring */
  218.         setlevel(fd,pups->kill.line,!pups->kill.inverted);
  219.         setlevel(fd,pups->cablepower.line,!pups->cablepower.inverted);
  220.  
  221.         /* Daemonize. */
  222.         switch(fork()) {
  223.                 case 0: /* Child */
  224.                         closelog();
  225.                         setsid();
  226.                         break;
  227.                 case -1: /* Error */
  228.                         syslog(LOG_ERR, "can't fork.");
  229.                         closelog();
  230.                         exit(1);
  231.                 default: /* Parent */
  232.                         closelog();
  233.                         exit(0);
  234.         }                                           /* switch(fork()) */
  235.  
  236.         /* Restart syslog. */
  237.         openlog(program_name, LOG_CONS, LOG_DAEMON);
  238.  
  239.         /* Create an info file for powerfail scripts. */
  240.         unlink(UPSSTAT);
  241.         if ((fd2 = open(UPSSTAT, O_CREAT|O_WRONLY, 0644)) >= 0) {
  242.                 write(fd2, "OK\n", 3);
  243.                 close(fd2);
  244.         }                        /* if ((fd2 = open(UPSSTAT, O_CREAT|O_WRONLY, 0644)) >= 0) */
  245.  
  246.         /* Now sample the line. */
  247.         while(1) {
  248.                 /* Get the status. */
  249.                 ioctl(fd, TIOCMGET, &flags);
  250.                 
  251.                 /* Calculate present status. */
  252.                 pstatus = getlevel(&pups->powerok,flags);
  253.                 bstatus = getlevel(&pups->battok,flags);
  254.  
  255.                 if (pups->cableok.line){
  256.                     /* Check the connection. */
  257.                     tries = 0;
  258.                     while(getlevel(&pups->cableok,flags) == 0) {
  259.                             /* Keep on trying, and warn every two minutes. */
  260.                             if ((tries % 60) == 0) syslog(LOG_ALERT, "UPS connection error");
  261.                             sleep(2);
  262.                             tries++;
  263.                             ioctl(fd, TIOCMGET, &flags);
  264.                     }                /* while(getlevel(&pups->cableok,flags) */
  265.                     if (tries > 0) syslog(LOG_ALERT, "UPS connection OK");
  266.                 } else {
  267.                     /* Do pseudo-cable check, bad power on startup == possible bad cable */
  268.                     if (tries < 1) {
  269.                             tries++;
  270.  
  271.                             /* Do startup failure check */
  272.                             if (!pstatus) {
  273.                                     /* Power is out: assume bad cable, but semi-scram to be safe */
  274.                                     syslog(LOG_ALERT, "No power on startup; UPS connection error?");
  275.                                 
  276.                                     /* Set status registers to prevent further processing until */
  277.                                     /* the status of the cable is changed.                      */
  278.                                     poldstat = pstatus;
  279.                                     boldstat = bstatus;
  280.                                 
  281.                                     powerfail(3);
  282.                             }                       /* if (!pstatus) */
  283.                     }                               /* if (tries < 1) */
  284.                 } /* if (pups->cableok.line) */
  285.  
  286.                 /* If anything has changed, process the change */
  287.                 if (pstatus != poldstat || bstatus != boldstat) {
  288.                     count++;
  289.                         if (count < 4) {
  290.                                 /* Wait a little to ride out short brown-outs */
  291.                                 sleep(1);
  292.                                 continue;
  293.                         }                               /* if (count < 4) */
  294.  
  295.                         if (pstatus != poldstat) {
  296.                                 if (pstatus) {
  297.                                         /* Power is OK */
  298.                                         syslog(LOG_ALERT, "Line power restored");
  299.                                         powerfail(0);
  300.                                 } else {
  301.                                         /* Power has FAILED */
  302.                                         if (bstatus) {
  303.                                                 /* Battery OK, normal shutdown */
  304.                                                 syslog(LOG_ALERT, "Line power has failed");
  305.                                                 powerfail(1);
  306.                                         } else {
  307.                                                 /* Low Battery, SCRAM! */
  308.                                                 syslog(LOG_ALERT, "UPS battery power is low!");
  309.                                                 powerfail(2);
  310.                                         }               /* if (bstatus) */
  311.                                 }                       /* if (pstatus) */
  312.                         }                               /* if (pstatus != poldstat) */
  313.         
  314.                         if (bstatus != boldstat) {
  315.                                 if (!bstatus && !pstatus) {
  316.                                         /* Power is out and Battery is now low, SCRAM! */
  317.                                         syslog(LOG_ALERT, "UPS battery power is low!");
  318.                                         powerfail(2);
  319.                                 }                       /* if (!bstatus && !pstatus) */
  320.                         }                               /* if (bstatus != boldstat) */
  321.         }                                       /* if (pstatus != poldstat || bstatus != boldstat) */
  322.  
  323.         
  324.         /* Reset count, remember status and sleep 2 seconds. */
  325.         count = 0;
  326.         poldstat = pstatus;
  327.         boldstat = bstatus;
  328.         sleep(2);
  329.     }       /* while(1) */
  330.     /* Never happens */
  331.     return(0);
  332. }                                                       /* main */
  333. /************************************************************************/
  334. /* End of Function main                                                 */
  335. /************************************************************************/
  336.  
  337. /************************************************************************/
  338. /* Function             : getlevel                                      */
  339. /* Author               : Erwin Authried <erwin@ws1.atv.tuwien.ac.at>   */
  340. /* Created              : 1995/06/19                                    */
  341. /* Last Modified By     :                             Date:             */
  342. /*                                                                      */
  343. /* Takes                : l: pointer to LINE-structure to test          */
  344. /*                      : flags: bits from ioctl-call                   */
  345. /*                                                                      */
  346. /* Returns              : int containing result of test.                */
  347. /*                      :                                               */
  348. /*                      : 1 == Tested line is in normal mode (active)   */
  349. /*                      : 0 == Tested line is in failure mode           */
  350. /*                                                                      */
  351. /* Purpose              : Tests the condition of a serial control line  */
  352. /*                      : to see if it is in normal or failure mode.    */
  353. /*                                                                      */
  354. /************************************************************************/ 
  355. int getlevel(LINE *l,int flags)
  356. {
  357. int ret;  
  358.  
  359.   if (!l->line) return 1;               /* normal mode */
  360.   ret = l->line & flags;
  361.   return (l->inverted) ? !ret: ret;
  362. }
  363.  
  364. /************************************************************************/
  365. /* Function             : setlevel                                      */
  366. /* Author               : Erwin Authried <erwin@ws1.atv.tuwien.ac.at>   */
  367. /* Created              : 1995/06/19                                    */
  368. /* Last Modified By     :                             Date:             */
  369. /*                                                                      */
  370. /* Takes                : fd:   filedescriptor of serial device         */
  371. /*                      : line: line to change                          */
  372. /*                      : level:new level                               */
  373. /*                      :       1==line is set (pos. voltage)           */
  374. /*                      :       0==line is cleared (neg. voltage)       */
  375. /*                                                                      */
  376. /* Returns              : -                                             */
  377. /*                      :                                               */
  378. /*                                                                      */
  379. /* Purpose              :  set/clear the specified output line          */
  380. /*                                                                      */
  381. /************************************************************************/ 
  382. void setlevel(int fd,int line,int level)
  383. {
  384. int bit;
  385.   if (line==0 || line==TIOCM_ST) return;
  386.   bit = line;
  387.   ioctl(fd, (level) ? TIOCMBIS:TIOCMBIC, &bit);
  388. }
  389.  
  390.  
  391. /************************************************************************/
  392. /* Function             : powerfail                                     */
  393. /* Author               : Miquel van Smoorenburg                        */
  394. /*                      :  <miquels@drinkel.nl.mugnet.org>              */
  395. /* Created              : 1993/10/28                                    */
  396. /* Last Modified By     : Tom Webster                 Date: 1995/01/21  */
  397. /*                      :  <webster@kaiwan.com>                         */
  398. /*                                                                      */
  399. /* Takes                : Failure mode as an int.                       */
  400. /* Returns              : None.                                         */
  401. /*                                                                      */
  402. /* Purpose              : Communicates power status to init via the     */
  403. /*                      : SIGPWR signal and status files.               */
  404. /*                                                                      */
  405. /************************************************************************/                                                
  406. void powerfail(int ok)
  407. {
  408.         int fd;
  409.  
  410.         /* Create an info file for init. */
  411.         unlink(PWRSTAT);
  412.         if ((fd = open(PWRSTAT, O_CREAT|O_WRONLY, 0644)) >= 0) {
  413.                 if (ok) {
  414.                         /* Problem */
  415.                         write(fd, "FAIL\n", 5);
  416.                 } else {
  417.                         /* No problem */
  418.                         write(fd, "OK\n", 3);
  419.                 }                                       /* if (ok) */
  420.                 close(fd);
  421.         }
  422.  
  423.         /* Create an info file for powerfail scripts. */
  424.         unlink(UPSSTAT);
  425.         if ((fd = open(UPSSTAT, O_CREAT|O_WRONLY, 0644)) >= 0) {
  426.                 switch(ok) {
  427.                         case 0: /* Power OK */
  428.                                 write(fd, "OK\n", 3);
  429.                                 break;
  430.                         case 1: /* Line Fail */
  431.                                 write(fd, "FAIL\n", 5);
  432.                                 break;
  433.                         case 2: /* Line Fail and Low Batt */
  434.                                 write(fd, "SCRAM\n", 6);
  435.                                 break;
  436.                         case 3: /* Bad cable? */
  437.                                 write(fd, "CABLE\n", 6);
  438.                                 break;
  439.                         default: /* Should never get here */
  440.                                 write(fd, "SCRAM\n", 6);
  441.                 }                                       /* Switch(ok) */
  442.                 close(fd);
  443.         }
  444.         kill(1, SIGPWR);
  445. } /*EOF powerfail */
  446. /************************************************************************/
  447. /* End of Function powerfail                                            */
  448. /************************************************************************/
  449.